package com.example.sanguogameserver.service;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.sanguogameserver.consts.RedisKeyConst;
import com.example.sanguogameserver.consts.SanGuoConst;
import com.example.sanguogameserver.dto.CurrentPlayer;
import com.example.sanguogameserver.dto.PlayerGeneralStartSumDTO;
import com.example.sanguogameserver.entity.AttackTask;
import com.example.sanguogameserver.entity.Player;
import com.example.sanguogameserver.entity.Server;
import com.example.sanguogameserver.entity.Territory;
import com.example.sanguogameserver.enums.EnableEnum;
import com.example.sanguogameserver.enums.HeadEnum;
import com.example.sanguogameserver.enums.OnlineEnum;
import com.example.sanguogameserver.enums.TitleEnum;
import com.example.sanguogameserver.mapper.PlayerMapper;
import com.example.sanguogameserver.request.PlayerRegister;
import com.example.sanguogameserver.request.UpdatePlayerInfo;
import com.example.sanguogameserver.response.MeInfoResp;
import com.example.sanguogameserver.response.SimplePlayerInfoResp;
import com.example.sanguogameserver.util.AuthUtil;
import com.example.sanguogameserver.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author baomidou
 * @since 2024-03-05
 */
@Service
@Slf4j
public class PlayerService extends ServiceImpl<PlayerMapper, Player> implements IService<Player> {

    @Autowired
    private RedisUtil redisUtil;

    @Lazy
    @Autowired
    private ChatService chatService;

    @Lazy
    @Autowired
    private ServerService serverService;

    @Lazy
    @Autowired
    private TerritoryService territoryService;

    @Lazy
    @Autowired
    private PlayerGeneralService playerGeneralService;

    @Lazy
    @Autowired
    private AttackTaskService attackTaskService;

    @Autowired
    private RedisLockRegistry redisLockRegistry;

    public List<Player> getByUser(Integer userId) {
        return lambdaQuery().eq(Player::getUserId, userId).list();
    }

    public Player getByZhuLuId(String zhuLuId) {
        return lambdaQuery().eq(Player::getZhuLuId, zhuLuId).last("limit 1").one();
    }

    public Player getByUserAndServer(Integer userId, Integer serverId) {
        return lambdaQuery().eq(Player::getUserId, userId).eq(Player::getServerId, serverId).one();
    }

    @Transactional
    public String enterServer(Integer userId, Integer serverId) {
        Player player = getByUserAndServer(userId, serverId);
        if (player == null) return "";
        String token = UUID.randomUUID().toString();
        setRedisPlayerInfoToken(player, token);
        return token;
    }

    @Transactional
    public String registerPlayer(PlayerRegister playerRegister) {


        Player player = getByUserAndServer(playerRegister.getUserId(), playerRegister.getServerId());
        Assert.isNull(player, "重复注册");
        Player playName = getByPlayName(playerRegister.getNickName());
        Assert.isNull(playName, "昵称已存在");
        Lock lock = redisLockRegistry.obtain(SanGuoConst.RedisLock.registerPlayer + playerRegister.getUserId());
        String token = "";
        if (lock.tryLock()) {

            try {
                player = new Player();
                player.setPlayName(playerRegister.getNickName());
                player.setCountryName(playerRegister.getCountryName());
                player.setZhuLuId(IdUtil.simpleUUID());
                player.setFlagColor(playerRegister.getFlagColor());
                player.setHead(HeadEnum.Head1);
                player.setTitle(TitleEnum.LV1);
                player.setTerritoryNum(0);
                player.setResourcesTotal(50000);
                player.setServerId(playerRegister.getServerId());
                player.setUserId(playerRegister.getUserId());
                player.setCreateDate(LocalDateTime.now());
                save(player);
                token = UUID.randomUUID().toString();
                setRedisPlayerInfoToken(player, token);
            } finally {
                //走到这里说明获取锁一定是成功的
                try {
                    lock.unlock();
                } catch (IllegalStateException e) {
                    //业务操作时间过长，会导致锁超时内系统释放，这里就会产生超时异常
                    log.error("释放时发现锁过期", e);
                }
            }
        } else {
            //走到这里说明获取锁一定是失败的的
            log.info("重复注册!:{}", JSON.toJSONString(playerRegister));
            Assert.isTrue(false, "注册中, 请稍等");
        }


        return token;
    }

    public Player getByPlayName(String name) {
        return lambdaQuery().eq(Player::getPlayName, name).last("limit 1").one();
    }

    public List<Player> getByLikePlayName(String searchContent) {
        return lambdaQuery().like(Player::getPlayName, searchContent).or().eq(Player::getZhuLuId, searchContent).list();
    }


    public void setRedisPlayerInfoToken(Player player, String token) {
       /* NumberFormat numberFormat = NumberFormat.getCompactNumberInstance(Locale.CHINA, NumberFormat.Style.LONG);
        numberFormat.setMinimumFractionDigits(2);*/
        CurrentPlayer currentPlayer = new CurrentPlayer();
        BeanUtils.copyProperties(player, currentPlayer);
        redisUtil.setObject(RedisKeyConst.AUTH + token, currentPlayer, RedisKeyConst.AUTH.getDuration());
        setOnline(player.getId());
    }

    public List<SimplePlayerInfoResp> findPlayerByName(String searchContent) {
        List<Player> players = getByLikePlayName(searchContent);
        List<SimplePlayerInfoResp> simplePlayerInfoResps = players.stream().map(item -> {
            SimplePlayerInfoResp simplePlayerInfoResp = new SimplePlayerInfoResp();
            BeanUtils.copyProperties(item, simplePlayerInfoResp);
            return simplePlayerInfoResp;
        }).collect(Collectors.toList());
        return simplePlayerInfoResps;
    }

    /**
     * 设置玩家在线状态
     */
    public void offline(Integer playerId) {
        lambdaUpdate().eq(Player::getId, playerId).set(Player::getOnline, OnlineEnum.Offline).update();
        Player player = getById(playerId);

        if (player.getAttackTaskId() != null) {
            AttackTask attackTask = attackTaskService.getById(player.getAttackTaskId());
            if (Objects.equals(attackTask.getAttackPlayerId(), player.getAttackTaskId())) {
                attackTask.setAttackIsEnter(EnableEnum.False);
            } else if (Objects.equals(attackTask.getBeAttackPlayerId(), player.getAttackTaskId())) {
                attackTask.setBeAttackIsEnter(EnableEnum.False);
            }
            attackTaskService.updateById(attackTask);
        }

    }

    public void online() {
        setOnline(AuthUtil.getCurrPlayerId());
        // 把消息放到redis
        chatService.dbToRedis(AuthUtil.getCurrPlayerId());

    }

    public void setOnline(Integer playerId) {
        lambdaUpdate().eq(Player::getId, playerId).set(Player::getOnline, OnlineEnum.online).update();
    }

    public MeInfoResp playerInfo(Integer playerId) {
        MeInfoResp meInfoResp = new MeInfoResp();
        Player player = getById(playerId);
        BeanUtils.copyProperties(player, meInfoResp);
        // 计算上线了多少天
        int betweenDay = LocalDateTimeUtil.betweenPeriod(player.getCreateDate().toLocalDate(), LocalDate.now()).getDays();
        meInfoResp.setCreateDay(betweenDay);
        Server server = serverService.getById(player.getServerId());
        meInfoResp.setServer(server);
        List<Territory> territories = territoryService.listByPlayerId(playerId);
        meInfoResp.setTerritories(territories);
        PlayerGeneralStartSumDTO playerGeneralStartSumDTO = playerGeneralService.getPlayerGeneralStartSumDTO(playerId);
        BeanUtils.copyProperties(playerGeneralStartSumDTO, meInfoResp);

        return meInfoResp;
    }

    public void updatePlayerInfo(UpdatePlayerInfo updatePlayerInfo) {
        if (StrUtil.isNotEmpty(updatePlayerInfo.getPlayName())) {
            Player playName = getByPlayName(updatePlayerInfo.getPlayName());
            Assert.isNull(playName, "昵称已存在");
        }
        lambdaUpdate().set(updatePlayerInfo.getHead() != null, Player::getHead, updatePlayerInfo.getHead()).set(updatePlayerInfo.getFlagColor() != null, Player::getFlagColor, updatePlayerInfo.getFlagColor()).set(updatePlayerInfo.getPlayName() != null, Player::getPlayName, updatePlayerInfo.getPlayName()).set(updatePlayerInfo.getCountryName() != null, Player::getCountryName, updatePlayerInfo.getCountryName()).eq(Player::getId, AuthUtil.getCurrPlayerId()).update();
        Player player = getById(AuthUtil.getCurrPlayerId());
        setRedisPlayerInfoToken(player, AuthUtil.getToken());
    }

    public void exit() {
        // 清除登录缓存
        redisUtil.delete(RedisKeyConst.AUTH + AuthUtil.getToken());
        offline(AuthUtil.getCurrPlayerId());
    }

    public Integer rewardResourcesTotal() {
        Player player = getById(AuthUtil.getCurrPlayerId());
        Integer addResources = 100;
        player.setResourcesTotal(player.getResourcesTotal() + addResources);
        lambdaUpdate()
                .set(Player::getResourcesTotal, player.getResourcesTotal())
                .eq(Player::getId, AuthUtil.getCurrPlayerId())
                .update();

        setRedisPlayerInfoToken(player, AuthUtil.getToken());
        return addResources;
    }
}
